﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using MEF_Test.Bricks;
using MEF_Test.Bricks.Actions;
using MEF_Test.Contracts;
using MEF_Test.Module.AudioStation.Tests.Properties;
using Microsoft.Practices.Unity;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Synology.AudioStationApi;

namespace MEF_Test.Module.AudioStation.Tests
{
    /// <summary>
    /// Summary description for BrowsingTests
    /// </summary>
    [TestClass]
    public class BrowsingTests
    {
        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext { get; set; }

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        [TestInitialize]
        public void IoCInitialize()
        {
            var actionManagerMock = new Mock<IActionsManager>();
            actionManagerMock.Setup(o => o.ExportedActions).Returns(
                new ObservableCollection<Lazy<INodeAction, IDictionary<string, object>>>());
            IoC.Current.RegisterInstance(actionManagerMock.Object);
        }
        [TestMethod]
        public void BrowsingProviderBrowseArtistChildrenTest()
        {
            // Create an artist 
            // Register a mock AudioStationSession that will return a list of albums when Browse() is called
            // Call GoToNode() passing artist as an argument.

            Mock<IAudioStationSession> synoMock = new Mock<IAudioStationSession>();
            IAudioStationSession syno = synoMock.Object;
            var albums = new List<SynoItem>();

            var artist = new SynoItem { ItemID = "music_artist/1", Title = "My Artist", IsContainer = true };

            albums.Add(new SynoTrack { Album = artist.ItemID, Title = "My Album" });

            synoMock.Setup(o => o.Browse("music_artist/1")).Returns(albums);

            IoC.Current.RegisterInstance(typeof(IAudioStationSession), syno);
            IEnumerable<IBrowserNodeViewModel> children = null;
            AudioStationBrowsingProvider browsingProvider = new AudioStationBrowsingProvider(
                new BrowserNodeViewModel<AudioStationFileHandler>(new AudioStationFileHandler(AudioStationAction.Browse, artist)),
                Settings.Default.SynoHost,
                Settings.Default.SynoPort);

            browsingProvider.CurrentChildrenChanged += (o, ea) =>
                                                           
                                                               children = ea.Children;
            browsingProvider.InvalidateChildren();
            Assert.IsNotNull(children);
            Assert.AreEqual(1, children.Count());
            Assert.AreEqual(albums[0].Title, ((AudioStationFileHandler)children.ElementAt(0).FileHandler).Title);
            
        }

        [TestMethod]
        public void BrowsingProviderBrowseAlbumChildrenTest()
        {
            Mock<IAudioStationSession> synoMock = new Mock<IAudioStationSession>();
            IAudioStationSession syno = synoMock.Object;
            var tracks = new List<SynoTrack>();

            var album = new SynoItem { ItemID = "music_album/1", Title = "My Album", IsContainer = true };

            tracks.Add(new SynoTrack { Album = album.ItemID, Title = "My Track" });
            synoMock.Setup(o => o.Browse("music_album/1")).Returns(tracks);

            IoC.Current.RegisterInstance(typeof(IAudioStationSession), syno);
            IEnumerable<IBrowserNodeViewModel> children = null;
            var startnode =
                new BrowserNodeViewModel<AudioStationFileHandler>(new AudioStationFileHandler(AudioStationAction.Browse, album));
            AudioStationBrowsingProvider browsingProvider = new AudioStationBrowsingProvider(startnode, Settings.Default.SynoHost, Settings.Default.SynoPort);
            browsingProvider.CurrentChildrenChanged += (o, ea) => children = ea.Children;
            browsingProvider.InvalidateChildren();
            Assert.IsNotNull(children);
            Assert.AreEqual(1, children.Count());
            Assert.AreEqual(tracks[0].Title, ((AudioStationFileHandler)children.ElementAt(0).FileHandler).Title);

        }


        [TestMethod]
        public void BrowseBackwardsNominalTest()
        {
            // Create an artist 
            // Register a mock AudioStationSession that will return a list of albums when Browse() is called
            // Call GoToNode() passing artist as an argument : chldren are albums
            // Call GoToNode() passing one of the current children as an argument.
            // Call GoToParent() and check that the children are albums

            Mock<IAudioStationSession> synoMock = new Mock<IAudioStationSession>();
            IAudioStationSession syno = synoMock.Object;
            var albums = new List<SynoItem>();
            
            var album = new SynoItem { ItemID = "music_album/1",  ItemPid = "music_artist/1",Title = "My Album", IsContainer = true };
            var artist = new SynoItem { ItemID = "music_artist/1",ItemPid = "music_artist", Title = "My Artist", IsContainer = true };
            
            albums.Add(album);
            albums.Add(album);
            albums.Add(album);

            var tracks = new List<SynoTrack>();
            tracks.Add(new SynoTrack { Album = artist.ItemID, Title = "My Track", ItemID = "music_lib_track/1", ItemPid = "music_album/1" });

            synoMock.Setup(o => o.Browse("music_artist")).Returns(albums);
            synoMock.Setup(o => o.Browse("music_artist/1")).Returns(albums);
            synoMock.Setup(o => o.Browse("music_album/1")).Returns(tracks);            
            IoC.Current.RegisterInstance(typeof(IAudioStationSession), syno);
            IEnumerable<IBrowserNodeViewModel> children = null;

            // The current node is an artist : its children will be albums.
            AudioStationBrowsingProvider browsingProvider = new AudioStationBrowsingProvider(
                new BrowserNodeViewModel<AudioStationFileHandler>(new AudioStationFileHandler(AudioStationAction.Browse, artist)),
                Settings.Default.SynoHost,
                Settings.Default.SynoPort);

            browsingProvider.CurrentChildrenChanged += (o, ea) =>
                                                           {
                                                               children = ea.Children;
                                                           };
            // make sure the albums are updated
            browsingProvider.InvalidateChildren();
            Assert.AreEqual(albums[0].Title, ((AudioStationFileHandler)children.ElementAt(0).FileHandler).Title);

            // go in the first album : the children are tracks
            browsingProvider.GoToNode(children.First());
            Assert.AreEqual(tracks[0].Title, ((AudioStationFileHandler)children.ElementAt(0).FileHandler).Title);
            
            // go back to the parent : the children will be albums 
            browsingProvider.GoToParentNode();
            
            
            Assert.IsNotNull(children);
            Assert.AreEqual(3, children.Count());
            Assert.AreEqual(albums[0].Title, ((AudioStationFileHandler)children.ElementAt(0).FileHandler).Title);
        }

        [TestMethod]
        public void BrowseBackwardsToAudiolibRootTest()
        {
            // Create an artist 
            // Register a mock AudioStationSession that will return a list of albums when Browse() is called
            // Call GoToNode() passing artist as an argument.
            // call GoToParentNode() to see if we get a category

            Mock<IAudioStationSession> synoMock = new Mock<IAudioStationSession>();
            IAudioStationSession syno = synoMock.Object;
            var categories = new List<SynoItem>();

            var category = new SynoItem { ItemID = "musiclib_music_aa", ItemPid = "musiclib_root", Title = "Artists Category", IsContainer = true };
            var category1 = new SynoItem { ItemID = "musiclib_music_album", ItemPid = "musiclib_root", Title = "Albums Category", IsContainer = true };
            var category2 = new SynoItem { ItemID = "musiclib_music_all", ItemPid = "musiclib_root", Title = "All Music Category", IsContainer = true };

            var artist = new SynoItem { ItemID = "musiclib_music_aa/1", ItemPid = "music_artist", Title = "My Artist", IsContainer = true };

            categories.Add(category);
            categories.Add(category1);
            categories.Add(category2);

            // if the artists category is requested : browse the artists
            synoMock.Setup(o => o.Browse("musiclib_music_aa")).Returns(new[] { artist });
            
            // if the music root is requested : browse the categories
            synoMock.Setup(o => o.Browse("musiclib_root")).Returns(categories);

            IoC.Current.RegisterInstance(typeof(IAudioStationSession), syno);
            IEnumerable<IBrowserNodeViewModel> children = null;

            // The current node is the artist category. : its children will be artists.
            AudioStationBrowsingProvider browsingProvider = new AudioStationBrowsingProvider(
                new BrowserNodeViewModel<AudioStationFileHandler>(new AudioStationFileHandler(AudioStationAction.Browse, category)),
                Settings.Default.SynoHost,
                Settings.Default.SynoPort);

            browsingProvider.CurrentChildrenChanged += (o, ea) =>
            {
                children = ea.Children;
            };
            // make sure the albums are updated
            browsingProvider.InvalidateChildren();
            Assert.AreEqual(artist.Title, ((AudioStationFileHandler)children.ElementAt(0).FileHandler).Title);
            Assert.AreEqual(1, children.Count());
           

            // go back to the parent : the children will be categories
            browsingProvider.GoToParentNode();


            Assert.IsNotNull(children);
            Assert.AreEqual(3, children.Count());
            Assert.AreEqual(categories[0].Title, ((AudioStationFileHandler)children.ElementAt(0).FileHandler).Title);


        }


        [TestMethod]
        public void BrowseBackwardsToAudiolibRootOutOfBoundsTest()
        {
            // Create an artist 
            // Register a mock AudioStationSession that will return a list of albums when Browse() is called
            // Call GoToNode() passing a category as an argument.
            // call GoToParentNode() to see if we get the root
            // call GoToParentNode() again to see if we still get the root

            Mock<IAudioStationSession> synoMock = new Mock<IAudioStationSession>();
            IAudioStationSession syno = synoMock.Object;
            var categories = new List<SynoItem>();

            var category = new SynoItem { ItemID = "musiclib_music_aa", ItemPid = "musiclib_root", Title = "Artists Category", IsContainer = true };
            var root = new SynoItem { ItemID = "musiclib_root", ItemPid = "musiclib_root", Title = "Categories", IsContainer = true };

            categories.Add(category);

            // if the music root is requested : browse the categories
            synoMock.Setup(o => o.Browse("musiclib_root")).Returns(categories);

            IoC.Current.RegisterInstance(typeof(IAudioStationSession), syno);
            IEnumerable<IBrowserNodeViewModel> children = null;

            // The current node is the artist category. : its children will be artists.
            AudioStationBrowsingProvider browsingProvider = new AudioStationBrowsingProvider(
                new BrowserNodeViewModel<AudioStationFileHandler>(new AudioStationFileHandler(AudioStationAction.Browse, root)),
                Settings.Default.SynoHost,
                Settings.Default.SynoPort);

            browsingProvider.CurrentChildrenChanged += (o, ea) =>
            {
                children = ea.Children;
            };
           
            // make sure the categories are updated
            browsingProvider.InvalidateChildren();
            Assert.AreEqual(category.Title, ((AudioStationFileHandler)children.ElementAt(0).FileHandler).Title);
            Assert.AreEqual(1, children.Count());

            // go back to the parent : the children will be categories
            browsingProvider.GoToParentNode();

            Assert.IsNotNull(children);
            Assert.AreEqual(1, children.Count());
            Assert.AreEqual(category.Title, ((AudioStationFileHandler)children.ElementAt(0).FileHandler).Title);

            // go back to the parent : the children will still be categories
            browsingProvider.GoToParentNode();

            Assert.IsNotNull(children);
            Assert.AreEqual(1, children.Count());
            Assert.AreEqual(category.Title, ((AudioStationFileHandler)children.ElementAt(0).FileHandler).Title);


        }

        [TestMethod]
        public void BrowseCategoriesTest()
        {
            // Create the audio root node
            var category1 = new SynoItem
            {
                Sequence = 0,
                IsContainer = true,
                ItemID = "musiclib_root",
                ItemPid = string.Empty,
                IsTrack = false,
                Title = "Toute la musique - This Should never be Displayed"
            };

            // Create a browserNode for category1
            IBrowserNodeViewModel bn = new BrowserNodeViewModel<AudioStationFileHandler>(new AudioStationFileHandler(AudioStationAction.Browse, category1));

            // Initialize real API
            var syno = new AudioStationSession(Settings.Default.SynoHost, Settings.Default.SynoPort);

            // Log in to API
            syno.Login(Settings.Default.Login, Settings.Default.Password);


            var fh = (AudioStationFileHandler)bn.FileHandler;

            // Request for sub-nodes of the audio_root node.
            var categoriesList = syno.Browse(fh.ItemID);

            // make sure there are more than one 
            Assert.IsTrue(categoriesList.Count() > 0);
        }

        [TestMethod]
        public void BrowseArtistTest()
        {
            // Initialize real API
            var syno = new AudioStationSession(Settings.Default.SynoHost, Settings.Default.SynoPort);

            // Log in to API
            syno.Login(Settings.Default.Login, Settings.Default.Password);

            // action=browse&target=musiclib_music_aa%2F1648185&server=musiclib_music_aa&category=&keyword=&start=0&limit=50&sort=title&dir=ASC
            SynoItem artist = new SynoItem
            {
                IsContainer = true,
                IsTrack = false,
                ItemID = "musiclib_music_aa/1648185",
                ItemPid = "musiclib_music_aa",
                Sequence = 0,
                Support = false,
                Title = "Caspian"
            };

            // Create a browserNode for category1
            BrowserNodeViewModel<AudioStationFileHandler> bn = new BrowserNodeViewModel<AudioStationFileHandler>(new AudioStationFileHandler(AudioStationAction.Browse, artist));

            // query albums
            var albumsList = syno.Browse(bn.FileHandler.ItemID);

            // make sure there is at least one album in the response.
            Assert.IsTrue(albumsList.Count() > 0);
        }

        [TestMethod]
        public void BrowseAlbumTest()
        {
            // Initialize real API
            var syno = new AudioStationSession(Settings.Default.SynoHost, Settings.Default.SynoPort);

            // Log in to API
            syno.Login(Settings.Default.Login, Settings.Default.Password);

            // action=browse&target=musiclib_music_aa%2F1648185&server=musiclib_music_aa&category=&keyword=&start=0&limit=50&sort=title&dir=ASC
            SynoItem album = new SynoItem
            {
                AlbumArtUrl = "webUI/getcover.cgi/artist/1648196",
                IsContainer = true,
                IsTrack = false,
                ItemID = "musiclib_music_aa/1648185/1648196",
                ItemPid = "musiclib_music_aa/1648185",
                Sequence = 0,
                Support = false,
                Title = "Tertia"
            };

            // Create a browserNode for category1
            BrowserNodeViewModel<AudioStationFileHandler> bn = new BrowserNodeViewModel<AudioStationFileHandler>(new AudioStationFileHandler(AudioStationAction.Browse, album));

            // query tracks
            var tracksList = syno.Browse(bn.FileHandler.ItemID);

            // make sure there is at least one track in the response.
            Assert.IsTrue(tracksList.Count() > 0);
        }
    }
}
